package com.mlethe.library.widget.progress;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

import androidx.annotation.IntDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 圆形进度条
 * @author Mlethe
 */
public class CircleProgressView extends View {

    @IntDef({ROUND, SQUARE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface RingType {
    }

    public static final int ROUND = 0x01;
    public static final int SQUARE = 0x02;

    private float mRingWidth;    // 圆环宽度
    private int mStartAngle;    // 圆环起始角度
    private int mSweepAngle;    // 圆环绘制角度
    private int mDefaultColor; // 默认颜色
    private int mProgressColor; // 进度条颜色
    private Drawable mCenterBackground; // 圆心背景颜色
    private int mTextColor; // 文字颜色
    private float mTextSize;
    @RingType
    private int mRingType;
    private float maxProgress = 1;
    private float progress = 0;
    private CircleProgressAnim anim;
    private int durationMillis = 0;
    private float useProgress;

    private TextListener mTextListener;

    private Paint mPaint;

    public CircleProgressView(Context context) {
        this(context, null);
    }

    public CircleProgressView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        anim = new CircleProgressAnim();
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView);
        mRingWidth = array.getDimension(R.styleable.CircleProgressView_RingWidth, dp2px(3));
        mStartAngle = array.getInt(R.styleable.CircleProgressView_RingStartAngle, 0);
        mSweepAngle = array.getInt(R.styleable.CircleProgressView_RingSweepAngle, 360);
        if (mSweepAngle > 360 || mSweepAngle < 0) {
            mSweepAngle = 360;
        }
        mDefaultColor = array.getColor(R.styleable.CircleProgressView_RingDefaultColor, Color.parseColor("#CCCCCC"));
        mProgressColor = array.getColor(R.styleable.CircleProgressView_RingProgressColor, Color.parseColor("#D81B60"));
        mCenterBackground = array.getDrawable(R.styleable.CircleProgressView_RingCenterBackground);
        mTextColor = array.getColor(R.styleable.CircleProgressView_ProgressTextColor, Color.BLACK);
        mTextSize = array.getDimension(R.styleable.CircleProgressView_ProgressTextSize, dp2px(14));
        maxProgress = array.getFloat(R.styleable.CircleProgressView_MaxProgress, maxProgress);
        progress = array.getFloat(R.styleable.CircleProgressView_Progress, progress);
        useProgress = progress;
        mRingType = array.getInt(R.styleable.CircleProgressView_RingType, ROUND);
        array.recycle();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 获取宽高的模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        // 1、确定的值，这个时候不需要计算，给的多少就是多少
        float width = MeasureSpec.getSize(widthMeasureSpec);
        float height = MeasureSpec.getSize(heightMeasureSpec);
        // 2、给的是wrap_content
        // MeasureSpec.AT_MOST：在布局中指定了wrap_content；MeasureSpec.EXACTLY：在布局中指定了确切的值，如100dp，match_parent，fill_parent；MeasureSpec.UNSPECIFIED：尽可能的大，很少能用到，如ListView，ScrollView
        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.EXACTLY) {
            width = dp2px(40);
        } else if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.AT_MOST) {
            height = dp2px(40);
        } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            width = dp2px(40);
            height = dp2px(40);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }
        widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) width, MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        float paddingLeft = getPaddingLeft();
        float paddingRight = getPaddingRight();
        float paddingTop = getPaddingTop();
        float paddingBottom = getPaddingBottom();
        float width = getWidth() - paddingLeft - paddingRight;
        float height = getHeight() - paddingTop - paddingBottom;
        float radius = Math.min(width, height) / 2f;
        float x = paddingLeft + (width + mRingWidth - radius * 2f) / 2f;
        float y = paddingTop + (height + mRingWidth - radius * 2f) / 2f;
        RectF oval = new RectF(x, y, x + radius * 2 - mRingWidth, y + radius * 2 - mRingWidth);
        mPaint.setStrokeWidth(mRingWidth);
        mPaint.setStyle(Paint.Style.STROKE);
        if (mRingType == SQUARE) {
            mPaint.setStrokeCap(Paint.Cap.SQUARE);
        } else {
            mPaint.setStrokeCap(Paint.Cap.ROUND);
        }
        drawDefaultRing(canvas, oval);
        drawProgressRing(canvas, oval);
        drawBackground(canvas, x + mRingWidth / 2f, y + mRingWidth / 2f, radius - mRingWidth);
        drawText(canvas, x, y, radius);
    }

    /**
     * 绘制默认圆环
     *
     * @param canvas
     * @param rectF
     */
    private void drawDefaultRing(Canvas canvas, RectF rectF) {
        mPaint.setColor(mDefaultColor);
        canvas.drawArc(rectF, mStartAngle, mSweepAngle, false, mPaint);
    }

    /**
     * 绘制进度圆环
     *
     * @param canvas
     * @param rectF
     */
    private void drawProgressRing(Canvas canvas, RectF rectF) {
        mPaint.setColor(mProgressColor);
        if (anim == null) {
            useProgress = progress;
        }
        if (mSweepAngle == 360) {
            canvas.drawArc(rectF, -90, useProgress / maxProgress * mSweepAngle, false, mPaint);
        } else {
            canvas.drawArc(rectF, mStartAngle, useProgress / maxProgress * mSweepAngle, false, mPaint);
        }
    }

    /**
     * 绘制中心背景
     *
     * @param canvas
     */
    private void drawBackground(Canvas canvas, float x, float y, float radius) {
        if (mCenterBackground == null) {
            return;
        }
        drawCircle(canvas, x, y, radius);
    }

    /**
     * 画圆
     *
     * @param canvas
     */
    private void drawCircle(Canvas canvas, float x, float y, float radius) {
        // 画圆
        Bitmap bitmap = drawableToBitmap(mCenterBackground, radius);
        // 把Bitmap变为圆形
        Bitmap circleBitmap = getCircleBitmap(bitmap, radius);
        // 把圆形的Bitmap绘制到画布上
        canvas.drawBitmap(circleBitmap, x, y, null);
        // 内存优化  回收Bitmap
        circleBitmap.recycle();
    }

    /**
     * 获取圆形Bitmap
     *
     * @param bitmap
     * @return
     */
    private Bitmap getCircleBitmap(Bitmap bitmap, float radius) {
        // 创建一个bitmap
        Bitmap circleBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(circleBitmap);
        Paint paint = new Paint();
        // 设置抗锯齿
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        // 设置防抖动
        paint.setDither(true);
        // 在画布上画个圆
        canvas.drawCircle(radius, radius, radius, paint);
        // 取圆和Bitmap的交集
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        // 再把原来的Bitmap绘制到新的圆上面
        canvas.drawBitmap(bitmap, 0, 0, paint);
        // 内存优化  回收Bitmap
        bitmap.recycle();
        return circleBitmap;
    }

    /**
     * 从Drawable中获得Bitmap
     *
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitmap(Drawable drawable, float radius) {
        // 如果是BitmapDrawable类型
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }
        // 其他类型 ColorDrawable
        // 创建一个什么也没有的Bitmap
        Bitmap outBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        // 创建一个画布
        Canvas canvas = new Canvas(outBitmap);
        // 把Drawable画到bitmap上
        drawable.setBounds(0, 0, (int) (radius * 2), (int) (radius * 2));
        drawable.draw(canvas);
        return outBitmap;
    }

    /**
     * 绘制文字
     *
     * @param canvas
     * @param startX
     * @param startY
     * @param radius
     */
    private void drawText(Canvas canvas, float startX, float startY, float radius) {
        String text;
        if (mTextListener != null) {
            text = mTextListener.getText(useProgress, maxProgress);
            if (text == null) {
                return;
            }
        } else {
            text = (int) (useProgress / maxProgress * 100) + "%";
        }
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);
        mPaint.setTextSize(mTextSize);
        mPaint.setColor(mTextColor);
        int textLength = text.length();
        Rect bounds = new Rect();
        mPaint.getTextBounds(text, 0, textLength, bounds);
        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        // top(给定字体大小的字体中最高字形的基线以上的最大距离) 是一个负值  bottom（给定字体大小下字体中最低字形的基线下方的最大距离） 是一个正值  descent 是一个正值  ascent 是一个负值
        int dy = (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent;
//        int dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        int baseLine = bounds.height() / 2 + dy;
        float x = startX + radius - (bounds.right - bounds.left) / 2f - bounds.left;
        float y = startY + radius - (bounds.bottom - bounds.top) / 2f + baseLine;
        canvas.drawText(text, 0, textLength, x, y, mPaint);
    }

    /**
     * dp转px
     *
     * @param dip
     * @return
     */
    private float dp2px(float dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
    }

    /**
     * sp转px
     * @param sp
     * @return
     */
    private float sp2px(float sp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    /**
     * 设置圆环宽度(单位：px)
     * @param ringWidth px
     * @return
     */
    public CircleProgressView setRingWidth(int ringWidth) {
        if (this.mRingWidth == ringWidth) {
            return this;
        }
        this.mRingWidth = ringWidth;
        postInvalidate();
        return this;
    }

    /**
     * 设置圆环宽度(单位：dp)
     * @param ringWidth dp
     * @return
     */
    public CircleProgressView setRingWidth(float ringWidth) {
        ringWidth = dp2px(ringWidth);
        if (this.mRingWidth == ringWidth) {
            return this;
        }
        this.mRingWidth = ringWidth;
        postInvalidate();
        return this;
    }

    /**
     * 设置默认圆环颜色
     * @param defaultColor
     * @return
     */
    public CircleProgressView setDefaultColor(int defaultColor) {
        if (this.mDefaultColor == defaultColor) {
            return this;
        }
        this.mDefaultColor = defaultColor;
        postInvalidate();
        return this;
    }

    /**
     * 设置进度颜色
     * @param progressColor
     * @return
     */
    public CircleProgressView setProgressColor(int progressColor) {
        if (this.mProgressColor == progressColor) {
            return this;
        }
        this.mProgressColor = progressColor;
        postInvalidate();
        return this;
    }

    /**
     * 设置中心背景
     * @param background
     * @return
     */
    public CircleProgressView setCenterBackground(Drawable background) {
        this.mCenterBackground = background;
        postInvalidate();
        return this;
    }

    /**
     * 设置文字颜色
     * @param textColor
     * @return
     */
    public CircleProgressView setTextColor(int textColor) {
        if (this.mTextColor == textColor) {
            return this;
        }
        this.mTextColor = textColor;
        postInvalidate();
        return this;
    }

    /**
     * 设置文字大小(单位：px)
     * @param textSize px
     * @return
     */
    public CircleProgressView setTextSize(int textSize) {
        if (this.mTextSize == textSize) {
            return this;
        }
        this.mTextSize = textSize;
        postInvalidate();
        return this;
    }

    /**
     * 设置文字大小(单位：sp)
     * @param textSize sp
     * @return
     */
    public CircleProgressView setTextSize(float textSize) {
        textSize = sp2px(textSize);
        if (this.mTextSize == textSize) {
            return this;
        }
        this.mTextSize = textSize;
        postInvalidate();
        return this;
    }

    /**
     * 设置样式
     * @param ringType
     * @return
     */
    public CircleProgressView setRingType(@RingType int ringType) {
        if (this.mRingType == ringType) {
            return this;
        }
        this.mRingType = ringType;
        postInvalidate();
        return this;
    }

    /**
     * 设置最大进度
     * @param maxProgress
     * @return
     */
    public CircleProgressView setMaxProgress(float maxProgress) {
        if (this.maxProgress == maxProgress) {
            return this;
        }
        this.maxProgress = maxProgress;
        postInvalidate();
        return this;
    }

    /**
     * 设置进度
     * @param progress
     * @return
     */
    public CircleProgressView setProgress(float progress) {
        if (this.progress == progress) {
            return this;
        }
        if (progress > maxProgress) {
            this.progress = maxProgress;
        } else if (progress < 0) {
            this.progress = 0;
        } else {
            this.progress = progress;
        }
        if (durationMillis > 0) {
            anim.cancel();
            anim.setDuration(durationMillis);
            this.startAnimation(anim);
        } else {
            float curProgress = progress;
            if (progress > maxProgress) {
                curProgress = maxProgress;
            }
            this.useProgress = curProgress;
            postInvalidate();
        }
        return this;
    }

    /**
     * 设置文字
     * @param textListener
     * @return
     */
    public CircleProgressView setTextListener(TextListener textListener) {
        this.mTextListener = textListener;
        return this;
    }

    /**
     * 设置动画时间
     * @param durationMillis
     * @return
     */
    public CircleProgressView setDuration(int durationMillis) {
        if (this.durationMillis == durationMillis) {
            return this;
        }
        this.durationMillis = durationMillis;
        if (durationMillis > 0) {
            useProgress = 0;
            anim.setDuration(durationMillis);
            this.startAnimation(anim);
        }
        return this;
    }

    public interface TextListener {
        String getText(float progress, float maxProgress);
    }

    public class CircleProgressAnim extends Animation {

        public CircleProgressAnim() {
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            float curProgress = progress;
            if (progress > maxProgress) {
                curProgress = maxProgress;
            }
            useProgress = interpolatedTime * (curProgress - useProgress) + useProgress;
            postInvalidate();
        }
    }
}
